/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file pgEntry.I
 * @author drose
 * @date 2002-03-13
 */

/**
 * Changes the text currently displayed within the entry.  This uses the
 * Unicode encoding currently specified for the "focus" TextNode; therefore,
 * the TextNode must exist before calling set_text().
 *
 * The return value is true if all the text is accepted, or false if some was
 * truncated (see set_max_width(), etc.).
 */
INLINE bool PGEntry::
set_text(const std::string &text) {
  LightReMutexHolder holder(_lock);
  TextNode *text_node = get_text_def(S_focus);
  nassertr(text_node != nullptr, false);
  return set_wtext(text_node->decode_text(text));
}

/**
 * Returns the text currently displayed within the entry, without any embedded
 * properties characters.
 *
 * This uses the Unicode encoding currently specified for the "focus"
 * TextNode; therefore, the TextNode must exist before calling get_text().
 */
INLINE std::string PGEntry::
get_plain_text() const {
  LightReMutexHolder holder(_lock);
  TextNode *text_node = get_text_def(S_focus);
  nassertr(text_node != nullptr, std::string());
  return text_node->encode_wtext(get_plain_wtext());
}

/**
 * Returns the text currently displayed within the entry.  This uses the
 * Unicode encoding currently specified for the "focus" TextNode; therefore,
 * the TextNode must exist before calling get_text().
 */
INLINE std::string PGEntry::
get_text() const {
  LightReMutexHolder holder(_lock);
  TextNode *text_node = get_text_def(S_focus);
  nassertr(text_node != nullptr, std::string());
  return text_node->encode_wtext(get_wtext());
}

/**
 * Returns the number of characters of text in the entry.  This is the actual
 * number of visible characters, not counting implicit newlines due to
 * wordwrapping, or formatted characters for text properties changes.  If
 * there is an embedded TextGraphic object, it counts as one character.
 *
 * This is also the length of the string returned by get_plain_text().
 */
INLINE int PGEntry::
get_num_characters() const {
  LightReMutexHolder holder(_lock);
  return _text.get_num_characters();
}

/**
 * Returns the character at the indicated position in the entry.  If the
 * object at this position is a graphic object instead of a character, returns
 * 0.
 */
INLINE wchar_t PGEntry::
get_character(int n) const {
  LightReMutexHolder holder(_lock);
  return _text.get_character(n);
}

/**
 * Returns the graphic object at the indicated position in the pre-wordwrapped
 * string.  If the object at this position is a character instead of a graphic
 * object, returns NULL.
 */
INLINE const TextGraphic *PGEntry::
get_graphic(int n) const {
  LightReMutexHolder holder(_lock);
  return _text.get_graphic(n);
}

/**
 * Returns the TextProperties in effect for the object at the indicated
 * position in the pre-wordwrapped string.
 */
INLINE const TextProperties &PGEntry::
get_properties(int n) const {
  LightReMutexHolder holder(_lock);
  return _text.get_properties(n);
}

/**
 * Sets the current position of the cursor.  This is the position within the
 * text at which the next letter typed by the user will be inserted; normally
 * it is the same as the length of the text.
 */
INLINE void PGEntry::
set_cursor_position(int position) {
  LightReMutexHolder holder(_lock);
  if (_cursor_position != position) {
    _cursor_position = position;
    _cursor_stale = true;
    _blink_start = ClockObject::get_global_clock()->get_frame_time();

#ifdef THREADED_PIPELINE
    if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
      update_cursor();
    }
#endif
  }
}

/**
 * Returns the current position of the cursor.
 */
INLINE int PGEntry::
get_cursor_position() const {
  LightReMutexHolder holder(_lock);
  return _cursor_position;
}

/**
 * Returns the node position x of the cursor
 */
INLINE PN_stdfloat PGEntry::
get_cursor_X() const {
  LightReMutexHolder holder(_lock);
  return _cursor_def.get_x();
}

/**
 * Returns the node position y of the cursor
 */
INLINE PN_stdfloat PGEntry::
get_cursor_Y() const {
  LightReMutexHolder holder(_lock);
  return _cursor_def.get_z();
}

/**
 * Sets the maximum number of characters that may be typed into the entry.
 * This is a limit on the number of characters, as opposed to the width of the
 * entry; see also set_max_width().
 *
 * If this is 0, there is no limit.
 */
INLINE void PGEntry::
set_max_chars(int max_chars) {
  LightReMutexHolder holder(_lock);
  _max_chars = max_chars;
}

/**
 * Returns the current maximum number of characters that may be typed into the
 * entry, or 0 if there is no limit.  See set_max_chars().
 */
INLINE int PGEntry::
get_max_chars() const {
  LightReMutexHolder holder(_lock);
  return _max_chars;
}

/**
 * Sets the maximum width of all characters that may be typed into the entry.
 * This is a limit on the width of the formatted text, not a fixed limit on
 * the number of characters; also set_max_chars().
 *
 * If this is 0, there is no limit.
 *
 * If _num_lines is more than 1, rather than being a fixed width on the whole
 * entry, this becomes instead the wordwrap width (and the width limit on the
 * entry is essentially _max_width * _num_lines).
 */
INLINE void PGEntry::
set_max_width(PN_stdfloat max_width) {
  LightReMutexHolder holder(_lock);
  _max_width = max_width;
  _text_geom_stale = true;

#ifdef THREADED_PIPELINE
  if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
    update_text();
    update_cursor();
  }
#endif
}

/**
 * Returns the current maximum width of the characters that may be typed into
 * the entry, or 0 if there is no limit.  See set_max_width().
 */
INLINE PN_stdfloat PGEntry::
get_max_width() const {
  LightReMutexHolder holder(_lock);
  return _max_width;
}

/**
 * Sets the number of lines of text the PGEntry will use.  This only has
 * meaning if _max_width is not 0; _max_width indicates the wordwrap width of
 * each line.
 */
INLINE void PGEntry::
set_num_lines(int num_lines) {
  LightReMutexHolder holder(_lock);
  nassertv(num_lines >= 1);
  if (_num_lines != num_lines) {
    _num_lines = num_lines;
    _text_geom_stale = true;
    update_text();

#ifdef THREADED_PIPELINE
    if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
      update_cursor();
    }
#endif
  }
}

/**
 * Returns the number of lines of text the PGEntry will use, if _max_width is
 * not 0.  See set_num_lines().
 */
INLINE int PGEntry::
get_num_lines() const {
  LightReMutexHolder holder(_lock);
  return _num_lines;
}

/**
 * Sets the number of times per second the cursor will blink while the entry
 * has keyboard focus.
 *
 * If this is 0, the cursor does not blink, but is held steady.
 */
INLINE void PGEntry::
set_blink_rate(PN_stdfloat blink_rate) {
  LightReMutexHolder holder(_lock);
  _blink_rate = blink_rate;
}

/**
 * Returns the number of times per second the cursor will blink, or 0 if the
 * cursor is not to blink.
 */
INLINE PN_stdfloat PGEntry::
get_blink_rate() const {
  LightReMutexHolder holder(_lock);
  return _blink_rate;
}

/**
 * Returns the Node that will be rendered to represent the cursor.  You can
 * attach suitable cursor geometry to this node.
 */
INLINE NodePath PGEntry::
get_cursor_def() {
  LightReMutexHolder holder(_lock);
  return _cursor_def;
}

/**
 * Removes all the children from the cursor_def node, in preparation for
 * adding a new definition.
 */
INLINE void PGEntry::
clear_cursor_def() {
  LightReMutexHolder holder(_lock);
  _cursor_def.remove_node();
  _cursor_def = _cursor_scale.attach_new_node("cursor");
}

/**
 * Sets whether the arrow keys (and home/end) control movement of the cursor.
 * If true, they are active; if false, they are ignored.
 */
INLINE void PGEntry::
set_cursor_keys_active(bool flag) {
  LightReMutexHolder holder(_lock);
  _cursor_keys_active = flag;
}

/**
 * Returns whether the arrow keys are currently set to control movement of the
 * cursor; see set_cursor_keys_active().
 */
INLINE bool PGEntry::
get_cursor_keys_active() const {
  LightReMutexHolder holder(_lock);
  return _cursor_keys_active;
}

/**
 * Specifies whether obscure mode should be enabled.  In obscure mode, a
 * string of asterisks is displayed instead of the literal text, e.g.  for
 * entering passwords.
 *
 * In obscure mode, the width of the text is computed based on the width of
 * the string of asterisks, not on the width of the actual text.  This has
 * implications on the maximum length of text that may be entered if max_width
 * is in effect.
 */
INLINE void PGEntry::
set_obscure_mode(bool flag) {
  LightReMutexHolder holder(_lock);
  if (_obscure_mode != flag) {
    _obscure_mode = flag;
    _text_geom_stale = true;

#ifdef THREADED_PIPELINE
    if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
      update_text();
      update_cursor();
    }
#endif
  }
}

/**
 * Specifies whether obscure mode is enabled.  See set_obscure_mode().
 */
INLINE bool PGEntry::
get_obscure_mode() const {
  LightReMutexHolder holder(_lock);
  return _obscure_mode;
}

/**
 * Specifies whether overflow mode should be enabled.  In overflow mode, text
 * can overflow the boundaries of the Entry element horizontally.
 *
 * Overflow mode only works when the number of lines is 1.
 */
INLINE void PGEntry::
set_overflow_mode(bool flag) {
  LightReMutexHolder holder(_lock);
  if (_overflow_mode != flag) {
    _overflow_mode = flag;
    _text_geom_stale = true;
    _cursor_stale = true;

#ifdef THREADED_PIPELINE
    if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
      update_text();
      update_cursor();
    }
#endif
  }
}

/**
 * Specifies whether overflow mode is enabled.  See set_overflow_mode().
 */
INLINE bool PGEntry::
get_overflow_mode() const {
  LightReMutexHolder holder(_lock);
  return _overflow_mode;
}

/**
 * Specifies the name of the TextProperties structure added to the
 * TextPropertiesManager that will be used to render candidate strings from
 * the IME, used for typing characters in east Asian languages.  Each
 * candidate string represents one possible way to interpret the sequence of
 * keys the user has just entered; it should not be considered typed yet, but
 * it is important for the user to be able to see what he is considering
 * entering.
 *
 * This particular method sets the properties for the subset of the current
 * candidate string that the user can actively scroll through.
 */
INLINE void PGEntry::
set_candidate_active(const std::string &candidate_active) {
  LightReMutexHolder holder(_lock);
  _candidate_active = candidate_active;
}

/**
 * See set_candidate_active().
 */
INLINE const std::string &PGEntry::
get_candidate_active() const {
  LightReMutexHolder holder(_lock);
  return _candidate_active;
}

/**
 * Specifies the name of the TextProperties structure added to the
 * TextPropertiesManager that will be used to render candidate strings from
 * the IME, used for typing characters in east Asian languages.  Each
 * candidate string represents one possible way to interpret the sequence of
 * keys the user has just entered; it should not be considered typed yet, but
 * it is important for the user to be able to see what he is considering
 * entering.
 *
 * This particular method sets the properties for the subset of the current
 * candidate string that the user is not actively scrolling through.
 */
INLINE void PGEntry::
set_candidate_inactive(const std::string &candidate_inactive) {
  LightReMutexHolder holder(_lock);
  _candidate_inactive = candidate_inactive;
}

/**
 * See set_candidate_inactive().
 */
INLINE const std::string &PGEntry::
get_candidate_inactive() const {
  LightReMutexHolder holder(_lock);
  return _candidate_inactive;
}

/**
 * Returns the prefix that is used to define the accept event for all
 * PGEntries.  The accept event is the concatenation of this string followed
 * by get_id().
 */
INLINE std::string PGEntry::
get_accept_prefix() {
  return "accept-";
}

/**
 * Returns the prefix that is used to define the accept failed event for all
 * PGEntries.  This event is the concatenation of this string followed by
 * get_id().
 */
INLINE std::string PGEntry::
get_accept_failed_prefix() {
  return "acceptfailed-";
}

/**
 * Returns the prefix that is used to define the overflow event for all
 * PGEntries.  The overflow event is the concatenation of this string followed
 * by get_id().
 */
INLINE std::string PGEntry::
get_overflow_prefix() {
  return "overflow-";
}

/**
 * Returns the prefix that is used to define the type event for all PGEntries.
 * The type event is the concatenation of this string followed by get_id().
 */
INLINE std::string PGEntry::
get_type_prefix() {
  return "type-";
}

/**
 * Returns the prefix that is used to define the erase event for all
 * PGEntries.  The erase event is the concatenation of this string followed by
 * get_id().
 */
INLINE std::string PGEntry::
get_erase_prefix() {
  return "erase-";
}

/**
 * Returns the prefix that is used to define the cursor event for all
 * PGEntries.  The cursor event is the concatenation of this string followed
 * by get_id().
 */
INLINE std::string PGEntry::
get_cursormove_prefix() {
  return "cursormove-";
}

/**
 * Returns the event name that will be thrown when the entry is accepted
 * normally.
 */
INLINE std::string PGEntry::
get_accept_event(const ButtonHandle &button) const {
  return get_accept_prefix() + button.get_name() + "-" + get_id();
}

/**
 * Returns the event name that will be thrown when the entry cannot accept an
 * input
 */
INLINE std::string PGEntry::
get_accept_failed_event(const ButtonHandle &button) const {
  return get_accept_failed_prefix() + button.get_name() + "-" + get_id();
}

/**
 * Returns the event name that will be thrown when too much text is attempted
 * to be entered into the PGEntry, exceeding either the limit set via
 * set_max_chars() or via set_max_width().
 */
INLINE std::string PGEntry::
get_overflow_event() const {
  return get_overflow_prefix() + get_id();
}

/**
 * Returns the event name that will be thrown whenever the user extends the
 * text by typing.
 */
INLINE std::string PGEntry::
get_type_event() const {
  return get_type_prefix() + get_id();
}

/**
 * Returns the event name that will be thrown whenever the user erases
 * characters in the text.
 */
INLINE std::string PGEntry::
get_erase_event() const {
  return get_erase_prefix() + get_id();
}

/**
 * Returns the event name that will be thrown whenever the cursor moves
 */
INLINE std::string PGEntry::
get_cursormove_event() const {
  return get_cursormove_prefix() + get_id();
}

/**
 * Changes the text currently displayed within the entry.
 *
 * The return value is true if all the text is accepted, or false if some was
 * truncated (see set_max_width(), etc.).
 */
INLINE bool PGEntry::
set_wtext(const std::wstring &wtext) {
  LightReMutexHolder holder(_lock);
  bool ret = _text.set_wtext(wtext);
  if (_obscure_mode) {
    ret = _obscure_text.set_wtext(std::wstring(_text.get_num_characters(), '*'));
  }
  _text_geom_stale = true;
#ifdef THREADED_PIPELINE
  if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
    update_text();
  }
#endif
  set_cursor_position((std::min)(_cursor_position, _text.get_num_characters()));
  return ret;
}

/**
 * Returns the text currently displayed within the entry, without any embedded
 * properties characters.
 */
INLINE std::wstring PGEntry::
get_plain_wtext() const {
  LightReMutexHolder holder(_lock);
  return _text.get_plain_wtext();
}

/**
 * Returns the text currently displayed within the entry.
 */
INLINE std::wstring PGEntry::
get_wtext() const {
  LightReMutexHolder holder(_lock);
  return _text.get_wtext();
}

/**
 * Sets whether the input may be accepted--use to disable submission by the
 * user
 */
INLINE void PGEntry::
set_accept_enabled(bool enabled) {
  LightReMutexHolder holder(_lock);
  _accept_enabled = enabled;
}
